package picounit.features.test_factory;

import picounit.DelegateTestResult;
import picounit.DelegatingTestResult;
import picounit.Lifecycle;
import picounit.Test;
import picounit.features.aggregate_exception.Thrower;
import picounit.features.mock_collaborator_injection.CollaboratorsInvocationHandler;
import picounit.features.mocking.MethodUtil;
import picounit.features.test_instantiator.Instantiator;
import picounit.features.test_instantiator.Invoker;
import picounit.features.test_instantiator.LifecycleInstantiator;
import picounit.mocker.MockControl;
import previous.picounit.TestCase;
import previous.picounit.Verify;

import java.lang.reflect.Method;

import junit.framework.TestResult;

public class PicoUnitTestCaseTest extends TestCase {
	private final Instantiator instantiator = injectCollaborator();
	private final Invoker invoker = injectCollaborator();
	private final MockControl mockControl = injectCollaborator();
	private final Thrower thrower = injectCollaborator();
	private final LifecycleInstantiator lifecycleInstantiator = injectCollaborator();
	private final TestListener testListener = injectCollaborator();
	private final CollaboratorsInvocationHandler collaboratorsInvocationHandler = injectCollaborator();
	
	private final DelegateTestResult delegateTestResult = injectCollaborator();
	private final Lifecycle lifecycle = injectCollaborator();
	
	private final TestResult testResult = new DelegatingTestResult(delegateTestResult);

	@SuppressWarnings("unchecked")
	private PicoUnitTestCase picoUnitTestCase(Method testMethod) {
		return new PicoUnitTestCase(testMethod.getDeclaringClass(), testMethod,
			instantiator, invoker, mockControl, thrower,
			lifecycleInstantiator, testListener,
			collaboratorsInvocationHandler);
	}

	public void testHasSingleTestCaseCount(Verify verify) {
		verify.that(picoUnitTestCase(TestExample.testExample).countTestCases()).isEqualTo(1);
	}

	public void testRun(previous.picounit.Should should) throws Exception {
		TestExample testExample = new TestExample();
		PicoUnitTestCase picoUnitTestCase = picoUnitTestCase(TestExample.testExample);

		delegateTestResult.startTest(picoUnitTestCase);

		collaboratorsInvocationHandler.clearMap();

		testListener
			.testStarted(TestExample.class.getName(), TestExample.testExample.getName());

		should.call(
			lifecycleInstantiator.instantiateLifecyclesFor(TestExample.class)
		).andReturn(lifecycle);

		mockControl.reset();

		invoker.invoke("setUp", lifecycle);

		should.call(
			instantiator.instantiate(TestExample.class)
		).andReturn(testExample);

		invoker.invoke("setUp", testExample);
		invoker.invoke("mock", testExample);

		thrower.clearError();

		invoker.invoke(TestExample.testExample, testExample);

		invoker.invoke("tearDown", testExample);
		invoker.invoke("tearDown", lifecycle);

		mockControl.verify();

		thrower.dispatchError();

		testListener
			.testFinished(TestExample.class.getName(), TestExample.testExample.getName());

		delegateTestResult.endTest(picoUnitTestCase);

		should.expectAboveWhenTheFollowingOccurs();

		picoUnitTestCase.run(testResult);
	}

	public void testExceptionsThrownDuringLifecycleSetupBecomeErrors(previous.picounit.Should should)
		throws Exception {

		PicoUnitTestCase picoUnitTestCase = picoUnitTestCase(TestExample.testExample);

		delegateTestResult.startTest(picoUnitTestCase);
		
		collaboratorsInvocationHandler.clearMap();
		
		testListener
			.testStarted(TestExample.class.getName(), TestExample.testExample.getName());

		IllegalArgumentException exception = new IllegalArgumentException();
		should.call(
			lifecycleInstantiator.instantiateLifecyclesFor(TestExample.class)
		).andRaise(exception);

		shouldCall:
			testListener.addError(TestExample.class, TestExample.testExample);
		
		delegateTestResult.addError(picoUnitTestCase, exception); 
		delegateTestResult.endTest(picoUnitTestCase);

		should.expectAboveWhenTheFollowingOccurs();

		picoUnitTestCase.run(testResult);
	}

	public void testIfTestAndTearDownThrowIncludeBothExceptionsInTestResult() {
	}

	public void testHasSameNameAsTestMethodWithClassName(Verify verify) {
		verify.that(picoUnitTestCase(TestExample.testExample).getName())
			.isEqualTo(TestExample.testExample.getName() + "(" + TestExample.class.getName() + ")");
	}

	public static class TestExample implements Test {
		public static final Method testExample = new MethodUtil().getMethod(TestExample.class,
			"testExample");

		public void testExample() {
		}
	}
}
